/*
 * 
 * 
 * @author: Oren Zamir
 */
package org.valire.usermanagement.services;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.valire.usermanagement.beans.Role;
import org.valire.usermanagement.beans.User;
import org.valire.util.lang.Args;



/**
 * The Class UserServiceImpl.
 */
public class UserServiceImpl implements UserService, UserDetailsService  {

	/** The Constant logger. */
	@SuppressWarnings("unused")
	private final static Log logger = LogFactory.getLog(UserServiceImpl.class);

	/** The user dao. */
	@Resource
	private UserDao userDao;
	
	/**
	 * Sets the user dao.
	 *
	 * @param userDao the new user dao
	 */
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}


	/* (non-Javadoc)
	 * @see org.valire.usermanagement.services.UserService#createUser(org.valire.usermanagement.beans.User)
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	@PreAuthorize("hasPermission(#newUser, 'org.valire.usermanagement.services.UserServiceImp.createUser(newUser)')")
	public User createUser(User newUser) {
		Args.assertNotNull(newUser.getUserName());
		
		User user = getUser(newUser.getUserName());
		if(user != null)
			throw new IllegalArgumentException("User '"+newUser.getUserName()+"' already exists");

		if (newUser.getPassword()==null)
			newUser.setPassword(createNewPasswordString(10));
		
		this.userDao.create(newUser);
		return newUser;
	}


	/* (non-Javadoc)
	 * @see org.valire.usermanagement.services.UserService#updateUser(org.valire.usermanagement.beans.User)
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	@PreAuthorize("hasPermission(#user, 'org.valire.usermanagement.services.UserServiceImp.updateUser(user)')")
	public void updateUser(User user) {
		Args.assertNotNull(user.getId());
		Args.assertNotNull(user.getUserName());

		// TODO : find an efficient way to check if the username has changed and if there is a duplicate.
//		User foundUser = getUser(user.getUserName());
//		if(foundUser != null)
//			throw new IllegalArgumentException("User '"+foundUser.getUserName()+"' already exists");
		userDao.merge(user);		
	}

	/* (non-Javadoc)
	 * @see org.valire.usermanagement.services.UserService#removeUser(org.valire.usermanagement.beans.User)
	 */
	@Override
	@Transactional(propagation = Propagation.REQUIRED)
	@PreAuthorize("hasPermission(#user, 'org.valire.usermanagement.services.UserServiceImp.removeUser(userName)')")
	public void removeUser(User userName) {
		this.userDao.setPersistentClass(User.class);
		this.userDao.delete(userName.getId());
	}

	/* (non-Javadoc)
	 * @see org.valire.usermanagement.services.UserService#getUser(java.lang.String)
	 */
	@Override
	@PreAuthorize("hasPermission(#userName, 'org.valire.usermanagement.services.UserServiceImp.getUser(userName)')")
	public User getUser(String userName) {
		return this.userDao.getUser(userName);
	}

	/* (non-Javadoc)
	 * @see org.valire.usermanagement.services.UserService#getUser(java.lang.Integer)
	 */
	@Override
	@PreAuthorize("hasPermission(#id, 'org.valire.usermanagement.services.UserServiceImp.getUser(id)')")
	public User getUser(Integer id) {
		return userDao.findById(id, false);
	}
	
	/* (non-Javadoc)
	 * @see org.valire.usermanagement.services.UserService#getAllUsers()
	 */
	@Override
	@PreAuthorize("hasPermission(null, 'org.valire.usermanagement.services.UserServiceImp.getAllUsers()')")
	public List<User> getAllUsers() {
		return userDao.findAll();
	}

	/* (non-Javadoc)
	 * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String)
	 */
	@Override
	public UserDetails loadUserByUsername(String inUserName) throws UsernameNotFoundException, DataAccessException {
		
		
		final User user= this.getUser(inUserName);
		
		if (user == null) throw new UsernameNotFoundException(inUserName = " was not found in the database!");
		
		return new UserDetails() {

			private static final long serialVersionUID = 1L;

			/* (non-Javadoc)
			 * @see org.springframework.security.core.userdetails.UserDetails#isEnabled()
			 */
			@Override
			public boolean isEnabled() 	{
				return user.isEnabled();
			}
			
			/* (non-Javadoc)
			 * @see org.springframework.security.core.userdetails.UserDetails#isCredentialsNonExpired()
			 */
			@Override
			public boolean isCredentialsNonExpired() {
				return user.isCredentialsNoneExpired();
			}
			
			/* (non-Javadoc)
			 * @see org.springframework.security.core.userdetails.UserDetails#isAccountNonLocked()
			 */
			@Override
			public boolean isAccountNonLocked() {
				return user.isAccountNonLocked();
			}
			
			/* (non-Javadoc)
			 * @see org.springframework.security.core.userdetails.UserDetails#isAccountNonExpired()
			 */
			@Override
			public boolean isAccountNonExpired() {
				return user.isAccountNonExpired();
			}
			
			/* (non-Javadoc)
			 * @see org.springframework.security.core.userdetails.UserDetails#getUsername()
			 */
			@Override
			public String getUsername() {
				return user.getUserName();
			}
			
			/* (non-Javadoc)
			 * @see org.springframework.security.core.userdetails.UserDetails#getPassword()
			 */
			@Override
			public String getPassword() {
				return user.getPassword();
			}
			
			/* (non-Javadoc)
			 * @see org.springframework.security.core.userdetails.UserDetails#getAuthorities()
			 */
			@Override
			public Collection<GrantedAuthority> getAuthorities() {
				
				Set<Role> roles = user.getRoles();

				ArrayList<GrantedAuthority>  ga = new ArrayList<GrantedAuthority>();
				Iterator<Role> iterator = roles.iterator();
				while (iterator.hasNext()) {
					Role role = (Role) iterator.next();
					ga.add(new GrantedAuthorityImpl(role.getRoleName()));					
				}
				return ga;
			}
		};
	}


	/**
	 * Gets the random string for passwords.
	 *
	 * @param length the length
	 * @return the random string
	 */
	private static String createNewPasswordString(int length) {
		final String charset = "!0123456789abcdefghijklmnopqrstuvwxyz";
		Random rand = new Random(System.currentTimeMillis());
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < length; i++) {
			int pos = rand.nextInt(charset.length());
			sb.append(charset.charAt(pos));
		}
		return sb.toString();
	}
	
	
	/* (non-Javadoc)
	 * @see org.valire.usermanagement.services.UserService#resetPassword(org.valire.usermanagement.beans.User)
	 */
	@Override
	public String resetPassword(User user) {
		String newPwd = createNewPasswordString(10);
		user.setPassword(newPwd);
		userDao.setPassword(user);
		return newPwd;
	}


}
